package common

import (
	"fmt"
	"sort"
	"sync"

	log "github.com/sirupsen/logrus"
)

type HerbDescription struct {
	Book        string // 书籍出处
	Description string // 药效描述
}

type Herb struct {
	Name         string            // 药材名称
	Link         string            // 药材采集链接
	Descriptions []HerbDescription // 药材特性描述
}

// 别名:3611

// 处方:44037
// 来源:50

// 制法:30427
// 炮制:616

// 功能主治:44062
// 功效:2
// 功用:2

// 摘录:44113

// 用法用量:42192

// 注意:3023
// 备注:369

// 临床应用:369
// 凉膈散:1
// 各家论述:1901
// 性状:193
// 拼音名:44
// 方论:1
// 禁忌:1
// 药理作用:32
// 规格:122
// 贮藏:193
// 黄连解毒汤:1

type PrescriptionDescription struct {
	Alias           string            `json:",omitempty"` //	别名
	Recipe          string            `json:",omitempty"` //	处方
	RecipeProcessed string            `json:",omitempty"` //	处方（清理后）
	Indication      string            `json:",omitempty"` //	功能主治
	Dosage          string            `json:",omitempty"` //	用量
	Process         string            `json:",omitempty"` //	制法、炮制
	Notes           string            `json:",omitempty"` //	注意事项
	Origin          string            `json:",omitempty"` //	摘录
	Book            string            `json:",omitempty"` //	书籍
	Volume          string            `json:",omitempty"` //	卷
	Name            string            `json:",omitempty"` //	方剂名
	Extra           map[string]string `json:",omitempty"` //	额外细节
	Raw             string            `json:"-"`          //	原始描述文本
}

type Prescription struct {
	Name         string                    //	方剂名称
	Link         string                    //   链接
	Descriptions []PrescriptionDescription //	描述列表
}

/////////////////////////////////////////
type Herbs []Herb

/////////////////////////////////////////

func (herbs *Herbs) LoadFromJSON(filename string) error {
	return LoadFromJSON(filename, herbs)
}

func (herbs Herbs) SaveToJSON(filename string) error {
	return SaveToJSON(filename, herbs)
}

func (herbs Herbs) SaveToCSV(filename string) error {
	records := [][]string{
		//	Header
		{
			//	一级
			"药材名",
			"链接",
			//	二级
			"书籍",
			"描述",
		},
	}

	for _, h := range herbs {
		for _, d := range h.Descriptions {
			records = append(records, []string{
				h.Name,
				h.Link,
				d.Book,
				d.Description,
			})
		}
	}

	return SaveToCSV(filename, records)
}

var lockHerbs sync.Mutex

func (herbs *Herbs) Add(h Herb) {
	lockHerbs.Lock()
	found := false
	for i, h1 := range *herbs {
		if h1.Name == h.Name {
			//	发现重名，合并重名解释
			log.Warnf("<发现重名> %s: 解释 [%d + %d]", h.Name, len(h1.Descriptions), len(h.Descriptions))
			h1.Descriptions = append(h1.Descriptions, h.Descriptions...)
			(*herbs)[i] = h1
			found = true
			break
		}
	}
	if !found {
		*herbs = append(*herbs, h)
	}
	lockHerbs.Unlock()
}

// Sort 将药材按长度倒序、名称正序
//	这样可以把最奇怪的药材放在前面
func (herbs *Herbs) Sort() {
	//	长度倒序、名称正序
	sort.SliceStable(*herbs, func(i, j int) bool {
		return CompareByLengthAndPinyin((*herbs)[i].Name, (*herbs)[j].Name)
	})
}

/////////////////////////////////////////
type Prescriptions []Prescription

/////////////////////////////////////////

func (pres *Prescriptions) LoadFromJSON(filename string) error {
	return LoadFromJSON(filename, pres)
}

func (pres Prescriptions) SaveToJSON(filename string) error {
	return SaveToJSON(filename, pres)
}

func (pres Prescriptions) SaveToCSV(filename string) error {
	records := [][]string{
		//	Header
		{
			//	一级
			"方剂名",
			"链接",
			//	二级
			"别名",
			"处方",
			"处方（处理后）",
			"功能主治",
			"用量",
			"制法",
			"注意事项",
			"摘录",
			"书籍",
			"卷",
		}}

	for _, p := range pres {
		for _, d := range p.Descriptions {
			records = append(records, []string{
				p.Name,
				p.Link,
				d.Alias,
				d.Recipe,
				d.RecipeProcessed,
				d.Indication,
				d.Dosage,
				d.Process,
				d.Notes,
				d.Origin,
				d.Book,
				d.Volume,
			})
		}
	}

	return SaveToCSV(filename, records)
}

// Sort 按照方剂拼音（正序）排序
func (pres *Prescriptions) Sort() {
	//	按照方剂拼音（正序）排序
	sort.SliceStable(*pres, func(i, j int) bool {
		return CompareByPinyin((*pres)[i].Name, (*pres)[j].Name)
	})
}

var lockPrescriptions sync.Mutex

func (pres *Prescriptions) Add(p Prescription) {
	lockPrescriptions.Lock()
	found := false
	for i, p1 := range *pres {
		if p1.Name == p.Name {
			//	发现重名，合并重名解释
			log.Warnf("<发现重名> %s: 解释 [%d + %d]", p.Name, len(p1.Descriptions), len(p.Descriptions))
			p1.Descriptions = append(p1.Descriptions, p.Descriptions...)
			(*pres)[i] = p1
			found = true
			break
		}
	}
	if !found {
		*pres = append(*pres, p)
	}
	lockPrescriptions.Unlock()
}

///////////////////////////////////////////////////////////////////////

type NameCounter struct {
	Name  string
	Count int
}

func (nc NameCounter) String() string {
	return fmt.Sprintf("%s/%d", nc.Name, nc.Count)
}

type NameCounters []NameCounter

func (ncs *NameCounters) Add(name string) {
	found := false
	for i, nc := range *ncs {
		if nc.Name == name {
			(*ncs)[i].Count = nc.Count + 1
			found = true
			break
		}
	}
	if !found {
		*ncs = append(*ncs, NameCounter{name, 1})
	}
}

func (ncs *NameCounters) AddNameCount(name string, count int) {
	found := false
	for i, nc := range *ncs {
		if nc.Name == name {
			(*ncs)[i].Count = count
			found = true
			break
		}
	}
	if !found {
		*ncs = append(*ncs, NameCounter{name, count})
	}
}

func (ncs *NameCounters) Sort() {
	sort.SliceStable(*ncs, func(i, j int) bool {
		if (*ncs)[i].Count == (*ncs)[j].Count {
			return CompareByPinyin((*ncs)[i].Name, (*ncs)[j].Name)
		}
		return (*ncs)[i].Count > (*ncs)[j].Count
	})
}
